﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace TireSizesCalculator.Model
{
    public class TireSizesList
    {
        private List<TireSize> _list;
        private SortedSet<int> _widths;
        private SortedSet<int> _profiles;
        private SortedSet<int> _radiuses;


        /// <summary>
        /// Konstruktor
        /// </summary>
        public TireSizesList()
        {
            _list = new List<TireSize>();
            _widths = new SortedSet<int>();
            _profiles = new SortedSet<int>();
            _radiuses = new SortedSet<int>();
        }


        /// <summary>
        /// Ilość dostępnych średnic felg
        /// </summary>
        public long RadiusesCount
        {
            get
            {
                return _radiuses.Count;
            }
        }

        /// <summary>
        /// Całkowita ilość wielkości kół
        /// </summary>
        public long Count
        {
            get
            {
                return _list.Count;
            }
        }


        /// <summary>
        /// Dodaje wielkość opony
        /// </summary>
        /// <param name="width">Szerokość opony</param>
        /// <param name="profile">Profil opony</param>
        /// <param name="radius">Średnica felgi</param>
        public void AddSize(int width, int profile, int radius)
        {
            TireSize tireSize = new TireSize(width, profile, radius);
            AddSize(tireSize);
        }

        /// <summary>
        /// Dodaje wielkość opony
        /// </summary>
        /// <param name="size">Wielkość opony zapisana w formacie "<szerokość>/<profil>R<średnica_felgi>"</param>
        public void AddSize(String size)
        {
            TireSize tireSize = new TireSize(size);
            AddSize(tireSize);
        }

        /// <summary>
        /// Dodaje wielkość opony
        /// </summary>
        /// <param name="size">Wielkość opony</param>
        public void AddSize(TireSize size)
        {
            _list.Add(size);

            _widths.Add(size.Width);
            _profiles.Add(size.Profile);
            _radiuses.Add(size.Radius);
        }

        /// <summary>
        /// Zwraca dostępne wielkości opony dla podanej średnicy felgi
        /// </summary>
        /// <param name="radius">Średnica felgi</param>
        /// <returns>Dostępne wielkości opon</returns>
        public List<TireSize> GetSizes(int radius)
        {
            var result = from elem in _list
                         where elem.Radius == radius
                         select elem;

            return result.ToList();
        }

        /// <summary>
        /// Zwraca wszystkie dostępne średnice felg
        /// </summary>
        /// <returns>Dostępne średnice felg</returns>
        public List<int> GetRadiuses()
        {
            return _radiuses.ToList();
        }

        /// <summary>
        /// Zwraca wszystkie dostępne szerokości opon
        /// </summary>
        /// <returns></returns>
        public List<int> GetWidths()
        {
            return _widths.ToList();
        }

        /// <summary>
        /// Zwraca wszystkie dostępne profile opon
        /// </summary>
        /// <returns></returns>
        public List<int> GetProfiles()
        {
            return _profiles.ToList();
        }


        public List<TireSize> GetList()
        {
            return _list;
        }


        public double GetMinTireSize()
        {
            double result = 0d;
            try
            {
                result = (from x in _list select x)
                    .Min(x => x.RadiusInMiliMeters);
            }
            catch { }

            return result;
        }

        public double GetMaxTireSize()
        {
            double result = 0d;
            try
            {
                result = (from x in _list select x)
                    .Max(x => x.RadiusInMiliMeters);
            }
            catch { }

            return result;
        }






        /// <summary>
        /// Zwraca wszystkie opony pasujące do podanego rozmiaru, które mieszczą się w granicach błędu (posortowane według najlepszego dopasowania)
        /// </summary>
        /// <param name="size">Idealny rozmiar</param>
        /// <param name="errPercent">Maksymalny stopień błędu (w procentach)</param>
        /// <returns>Lista pasujących opon posortowana według najlepszego dopasowania</returns>
        public List<TireMatch> GetSizesFor(TireSize size, double errPercent)
        {
            double min = (size.WheelSize * (100d - errPercent)) / 100d;
            double max = (size.WheelSize * (100d + errPercent)) / 100d;

            var result = from elem in _list
                       let error = (Math.Abs(elem.WheelSize - size.WheelSize) * 100d / size.WheelSize)
                       where error <= errPercent && (elem.Width != size.Width || elem.Profile != size.Profile || elem.Radius != size.Radius)
                       orderby error ascending
                       select new TireMatch(elem, error, new RimsSize(elem));

            return result.ToList();
        }

        /// <summary>
        /// Sprawdzenie poprawności rozmiaru opony
        /// </summary>
        /// <param name="size">Rozmiar opony do sprawdzenia</param>
        /// <returns>Flaga określająca poprawność danych</returns>
        public bool CheckSize(TireSize size)
        {
            var result = from elem in _list
                         where elem.Equals(size)
                         select elem;

            return (result.Count() > 0);
        }
    }
}
